﻿using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Reflection;
using System.Threading.Tasks;
using YWM.Common.Extensions;

namespace YWM.Net.Http
{
    public static class HttpClientExtensions
    {
        public static async Task<HttpResult<TResult>> GetAsync<TResult>(this HttpClient httpClient, HttpConfig httpConfig, IHttpInterceptor httpInterceptor = null)
        {
            HttpResult<TResult> httpResult = await httpClient.SendAsync<TResult>(httpConfig, HttpMethodType.Get, httpInterceptor);
            return httpResult;
        }

        public static async Task<HttpResult<TResult>> PostAsync<TResult>(this HttpClient httpClient, HttpConfig httpConfig, IHttpInterceptor httpInterceptor = null)
        {
            HttpResult<TResult> httpResult = await httpClient.SendAsync<TResult>(httpConfig, HttpMethodType.Post, httpInterceptor);
            return httpResult;
        }

        public static async Task<HttpResult<TResult>> PostMultipartFormDataAsync<TResult>(this HttpClient httpClient, MultipartFormDataConfig httpConfig, IHttpInterceptor httpInterceptor = null)
        {
            HttpResult<TResult> httpResult = await httpClient.SendMultipartFormDataAsync<TResult>(httpConfig, HttpMethodType.Post, httpInterceptor);
            return httpResult;
        }

        public static async Task<HttpResult<TResult>> PutAsync<TResult>(this HttpClient httpClient, HttpConfig httpConfig, IHttpInterceptor httpInterceptor = null)
        {
            HttpResult<TResult> httpResult = await httpClient.SendAsync<TResult>(httpConfig, HttpMethodType.Put, httpInterceptor);
            return httpResult;
        }

        public static async Task<HttpResult<TResult>> PutMultipartFormDataAsync<TResult>(this HttpClient httpClient, MultipartFormDataConfig httpConfig, IHttpInterceptor httpInterceptor = null)
        {
            HttpResult<TResult> httpResult = await httpClient.SendMultipartFormDataAsync<TResult>(httpConfig, HttpMethodType.Put, httpInterceptor);
            return httpResult;
        }

        public static async Task<HttpResult<TResult>> DeleteAsync<TResult>(this HttpClient httpClient, HttpConfig httpConfig, IHttpInterceptor httpInterceptor = null)
        {
            HttpResult<TResult> httpResult = await httpClient.SendAsync<TResult>(httpConfig, HttpMethodType.Delete, httpInterceptor);
            return httpResult;
        }

        private static async Task<HttpResult<TResult>> SendAsync<TResult>(this HttpClient httpClient, HttpConfig httpConfig, HttpMethodType httpMethodType, IHttpInterceptor httpInterceptor = null)
        {
            HttpResponseMessage httpResponseMessage = null;
            HttpResult<TResult> httpResult;
            try
            {
                HttpRequestMessage httpRequestMessage = new HttpRequestMessage
                {
                    RequestUri = new Uri(httpConfig.RequestUri),
                };
                httpRequestMessage.AddHttpRequestHeaders(httpConfig.Headers);
                if (httpInterceptor != null)
                {
                    httpRequestMessage = await httpInterceptor.RequestAsync(httpRequestMessage);
                }
                switch (httpMethodType)
                {
                    case HttpMethodType.Get:
                        httpRequestMessage.Method = HttpMethod.Get;
                        httpResponseMessage = await httpClient.SendAsync(httpRequestMessage);
                        break;
                    case HttpMethodType.Post:
                        httpRequestMessage.Method = HttpMethod.Post;
                        httpRequestMessage.SetHttpContent(httpConfig);
                        httpResponseMessage = await httpClient.SendAsync(httpRequestMessage);
                        break;
                    case HttpMethodType.Put:
                        httpRequestMessage.Method = HttpMethod.Put;
                        httpRequestMessage.SetHttpContent(httpConfig);
                        httpResponseMessage = await httpClient.SendAsync(httpRequestMessage);
                        break;
                    case HttpMethodType.Delete:
                        httpRequestMessage.Method = HttpMethod.Delete;
                        httpResponseMessage = await httpClient.SendAsync(httpRequestMessage);
                        break;
                }
                httpResult = await httpResponseMessage.GetResponseAsync<TResult>();
            }
            catch (Exception e)
            {
                int code;
                if (e is HttpRequestException)
                {
                    code = HttpErrorCode.RequestError;
                }
                else
                {
                    code = HttpErrorCode.GetRequestContentError;
                }
                httpResult = new HttpResult<TResult>
                {
                    Code = code,
                    Message = e.Message
                };
            }

            return httpResult;
        }

        private static async Task<HttpResult<TResult>> SendMultipartFormDataAsync<TResult>(this HttpClient httpClient, MultipartFormDataConfig httpConfig, HttpMethodType httpMethodType, IHttpInterceptor httpInterceptor = null)
        {
            HttpResponseMessage httpResponseMessage = null;
            HttpResult<TResult> httpResult;
            try
            {
                HttpRequestMessage httpRequestMessage = new HttpRequestMessage
                {
                    RequestUri = new Uri(httpConfig.RequestUri),
                };
                httpRequestMessage.AddHttpRequestHeaders(httpConfig.Headers);
                if (httpInterceptor != null)
                {
                    httpRequestMessage = await httpInterceptor.RequestAsync(httpRequestMessage);
                }
                switch (httpMethodType)
                {
                    case HttpMethodType.Post:
                        httpRequestMessage.Method = HttpMethod.Post;
                        httpRequestMessage.SetMultipartFormDataContent(httpConfig);
                        httpResponseMessage = await httpClient.SendAsync(httpRequestMessage);
                        break;
                    case HttpMethodType.Put:
                        httpRequestMessage.Method = HttpMethod.Put;
                        httpRequestMessage.SetMultipartFormDataContent(httpConfig);
                        httpResponseMessage = await httpClient.SendAsync(httpRequestMessage);
                        break;
                }
                httpResult = await httpResponseMessage.GetResponseAsync<TResult>();
            }
            catch (Exception e)
            {
                int code;
                if (e is HttpRequestException)
                {
                    code = HttpErrorCode.RequestError;
                }
                else
                {
                    code = HttpErrorCode.GetRequestContentError;
                }
                httpResult = new HttpResult<TResult>
                {
                    Code = code,
                    Message = e.Message
                };
            }

            return httpResult;
        }

        public static void SetBearerToken(this HttpRequestMessage httpRequestMessage, string token)
        {
            httpRequestMessage.SetToken(HttpAuthorizationScheme.Bearer, token);
        }

        public static void SetToken(this HttpRequestMessage httpRequestMessage, string scheme, string token)
        {
            httpRequestMessage.Headers.Authorization = new AuthenticationHeaderValue(scheme, token);
        }

        private static void AddHttpRequestHeaders(this HttpRequestMessage httpRequestMessage, IDictionary<string, string> headers)
        {
            if (headers != null)
            {
                foreach (KeyValuePair<string, string> header in headers)
                {
                    httpRequestMessage.Headers.Add(header.Key, header.Value);
                }
            }
        }

        private static void SetHttpContent(this HttpRequestMessage httpRequestMessage, HttpConfig httpConfig)
        {
            if (httpConfig.Data.IsNullOrWhiteSpace())
            {
                return;
            }
            httpRequestMessage.Content = new StringContent(httpConfig.Data, httpConfig.ContentEncoding);
            httpRequestMessage.Content.Headers.ContentType = new MediaTypeHeaderValue(httpConfig.ContentType);
        }

        private static void SetMultipartFormDataContent(this HttpRequestMessage httpRequestMessage, MultipartFormDataConfig multipartFormDataConfig)
        {
            MultipartFormDataContent multipartFormDataContent = new MultipartFormDataContent();
            if (multipartFormDataConfig.InputModel != null)
            {
                IEnumerable<PropertyInfo> propertyInfos = multipartFormDataConfig.InputModel.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(pi => pi.CanRead);
                foreach (PropertyInfo propertyInfo in propertyInfos)
                {
                    object objValue = propertyInfo.GetValue(multipartFormDataConfig.InputModel);
                    if (objValue == null)
                    {
                        continue;
                    }

                    Type type = propertyInfo.PropertyType;
                    string value = null;
                    if (objValue is DateTime)
                    {
                        value = Convert.ToDateTime(objValue).ToString("yyyy-MM-dd'T'HH:mm:ss");
                    }
                    else if (type.IsValueType || objValue is string)
                    {
                        value = objValue.ToString();
                    }

                    if (value != null)
                    {
                        multipartFormDataContent.Add(new StringContent(value, multipartFormDataConfig.ContentEncoding), propertyInfo.Name);
                    }
                }
            }

            if (multipartFormDataConfig.Params != null)
            {
                foreach (MultipartFormDataContentModel multipartFormDataContentModel in multipartFormDataConfig.Params)
                {
                    if (multipartFormDataContentModel==null || multipartFormDataContentModel.HttpContent == null || multipartFormDataContentModel.Name.IsNullOrWhiteSpace())
                    {
                        continue;
                    }

                    if (multipartFormDataContentModel.FileName.IsNullOrWhiteSpace())
                    {
                        multipartFormDataContent.Add(multipartFormDataContentModel.HttpContent, multipartFormDataContentModel.Name);
                    }
                    else
                    {
                        multipartFormDataContent.Add(multipartFormDataContentModel.HttpContent, multipartFormDataContentModel.Name, multipartFormDataContentModel.FileName);
                    }
                }
            }

            httpRequestMessage.Content = multipartFormDataContent;
        }

        private static async Task<HttpResult<TResult>> GetResponseAsync<TResult>(this HttpResponseMessage httpResponseMessage)
        {
            HttpResult<TResult> httpResult;
            try
            {
                HttpContent responseContent = httpResponseMessage.Content;
                string content = await responseContent.ReadAsStringAsync();
                httpResult = new HttpResult<TResult>
                {
                    Code = Convert.ToInt32(httpResponseMessage.StatusCode),
                    Message = httpResponseMessage.ReasonPhrase,
                    Data = JsonConvert.DeserializeObject<TResult>(content)
                };
            }
            catch (Exception e)
            {
                httpResult = new HttpResult<TResult>
                {
                    Code = HttpErrorCode.JsonConvertObjectError,
                    Message = e.Message
                };
            }
            return httpResult;
        }
    }
}